/**
 * @file cmap.c
 * @brief Implementa le funzioni di una struttura dati Map Concorrenziale.
 *
 * Complete description of this file.
 *
 * @date Mar 16, 2012
 * @author Francesco Corvino
 */
#include "cmap.h"
#include "../logproxy/logproxy.h"
#include "hiddenType.h"

int _CMap_orderByKey(const void * a , const void * b){
	int A = 0,B = 0;
	if(a!=NULL) A = ((CMapElem*) a)->key;
	if(b!=NULL) B = ((CMapElem*) b)->key;
	return (A>B)?1:((B==A)?0:-1);
}

void _CMap_initSync(pthread_mutex_t * mtx){
	// per cambiare la gestione della sincronizzazione sostituire il
	// NULL con l'opportuno attributo
	if(pthread_mutex_init(mtx,NULL)!=0){
		log_level(LOG_ERROR_LEVEL,
				"impossibile abilitare le funzionalità di accesso concorrenziale");
	}
}

//void _CMap_commitOp(pthread_mutex_t * mtx, void (*op)()){
//	pthread_mutex_lock(mtx);
//	(*op)();
//	pthread_mutex_unlock(mtx);
//}

void _CMap_lock(pthread_mutex_t * mtx){
	pthread_mutex_lock(mtx);
}

void _CMap_unlock(pthread_mutex_t * mtx){
	pthread_mutex_unlock(mtx);
}

CMapElem * _CMap_createElem(const int key, void * data, size_t dim){
	CMapElem *ret; void* cdata;
	if((ret = malloc(sizeof(CMapElem)))!=NULL) {
		if((cdata=malloc(dim))!=NULL) {
			memcpy(cdata,data,dim);
		} else cdata = data;
		ret->key = key;
		ret->dimdata = dim;
		ret->data = cdata;
		_CMap_initSync(&(ret->synelem));
	} else return NULL;
	return ret;
}

void _CMap_modifyElem(CMapElem * el, void *data, size_t newdim){
	_CMap_lock(&(el->synelem));
	void * cdata;
	free(el->data);
	if((cdata=malloc(newdim))!=NULL) {
		memcpy(cdata,data,newdim);
	} else cdata = data;
	el->dimdata = newdim;
	el->data = cdata;
	_CMap_unlock(&(el->synelem));
}

void CMap_init(CMap * cmap){
	cmap->values.cmpElem = _CMap_orderByKey;
	cmap->values.sizeSingleElem = sizeof(CMapElem);
	cmap->values.elemBuff = NULL;
	cmap->values.numelement = 0;
	_CMap_initSync(&(cmap->synarr));
}

/**
 * @brief Libera la memoria allocata da una mappa.
 */
void CMap_destroy(CMap * cmap){
	DynArray_destroy(&(cmap->values));
	//TODO libera la memoria allocata
}

void* CMap_get(CMap * cmap, const int key){
	CMapElem *ret = DynArray_get(&(cmap->values),&key);
	if(ret==NULL) return NULL;
	return ret->data;
}

/**
 * @brief Restituisce i dati del valore successivo nella mappa
 *
 * @param cmap mappa da controllare
 * @param index indice dell'elemento corrente
 */
void* CMap_getNext(CMap *cmap, int *index){
	_CMap_lock(&(cmap->synarr));
	CMapElem* elem = (CMapElem*) DynArray_getNext(&(cmap->values),index);
	_CMap_unlock(&(cmap->synarr));
	if(elem==NULL) return NULL;
	return elem->data;
}

void * CMap_getByIndex(CMap * cmap, const int index){
	CMapElem *ret = (CMapElem*) DynArray_getIndex(&(cmap->values),index);
	if(ret==NULL) return NULL;
	return ret->data;
}

int CMap_size(CMap * cmap){
	return cmap->values.numelement;
}

void CMap_set(CMap * cmap, const int key, void * data, size_t dim){
	CMapElem tmpelem; tmpelem.key = key;
	CMapElem * elifexist = (CMapElem*) DynArray_get(&(cmap->values),&tmpelem);
	if(elifexist==NULL){ // elemento nuovo
		_CMap_lock(&(cmap->synarr));
			CMapElem *newel = _CMap_createElem(key,data,dim);
			if(newel!=NULL) DynArray_add(&(cmap->values),newel);
		_CMap_unlock(&(cmap->synarr));
	} else { // sostituisce l'elemento
		_CMap_modifyElem(elifexist,data,dim);
	}
}

/**
 * @brief Esegue un'operazione concorrenziale su un elemento della mappa.
 *
 * @param cmap mappa a cui appartiene l'elemento.
 * @param key chiave da ricercare
 * @param op operazione da eseguire
 *
 * @return -1 se l'operazione non è stata eseguita, altrimenti restituisce il risultato dell'operazione
 */
//int CMap_operateOn(CMap * cmap, const int key, int (*op)(void*)){
//	int ret = 0;
//	void* data = NULL; pthread_mutex_t * syncElem;
//	// cerca l'elemento su cui operare
//	CMapElem tmpelem; tmpelem.key = key;
//	CMapElem* opelem = (CMapElem*) DynArray_get(&(cmap->values), &tmpelem);
//	if(opelem==NULL) { // definisce l'operazione sull'intera mappa
//		syncElem = &(cmap->synarr);
//	} else {	// definisce l'operazione sull'elemento
//		data = opelem->data;
//		syncElem = &(opelem->synelem);
//	}
//	void myOp(){ ret = (*op)(data);}
//	_CMap_commitOp(syncElem,myOp);
//	return ret;
//}

/**
 * @brief Controlla se una mappa ha un attributo con una determinata chiave.
 *
 * @param cmap mappa da controllare.
 * @param key chiave da individuare.
 *
 * @return 1 se l'elmento è presente nella mappa, -1 altrimenti
 */
int CMap_has(CMap * cmap, const int key){
	CMapElem tmpelem;
	tmpelem.key = key;
	if(DynArray_get(&(cmap->values), &tmpelem)==NULL) return -1;
	return 1;
}

/**
 * @brief Rimuove un elemento dalla mappa.
 *
 * @note Nell'implementazione al momento la concorrenzialità viene gestita accedendo
 * al mutex che protegge la root degli elementi.
 *
 * @todo Si dovrebbe controllare se il mutex che protegge l'elemento da eliminare
 * è sotto lock da parte di altri.
 *
 * @param cmap mappa da modificare.
 * @param key chiave dell'elemento da rimuovere.
 * @return ritorna il risultato dell'operazione svolta da @see DynArray_remove
 */
int CMap_remove(CMap * cmap, const int key){
	CMapElem tmpelem; tmpelem.key = key;
	CMapElem *delelem = DynArray_get(&(cmap->values),&tmpelem);
	if(delelem==NULL) return -1;
	int ret = 0;
	_CMap_lock(&(cmap->synarr));
		free(delelem->data);
		ret = DynArray_remove(&(cmap->values),delelem);
	_CMap_unlock(&(cmap->synarr));
	return ret;
}

/**
 * @brief Converte una CMap in DynArray.
 *
 * Gli elementi sono forzati ad essere della dimensione <code>dimSingleElem</code>.
 *
 * @param cmap oggetto da convertire.
 * @param dimSingleElem dimensione degli oggetti da ricavare.
 */
DynArray* CMap_toArray(CMap * cmap, size_t dimSingleElem){
	DynArray *ptrRet; int i;
	ptrRet = malloc(sizeof(DynArray));
	//TODO gestire fallimenti di allocazione memoria
	DynArray_init(ptrRet,NULL,dimSingleElem);
	for(i=0;i<cmap->values.numelement;i++){
		CMapElem *tmp = (CMapElem*) DynArray_getIndex(&(cmap->values),i);
		DynArray_add(ptrRet,tmp->data);
	}
	return ptrRet;
}

/**
 * @brief Funzione utilizzata per ottenere un'intero rappresentativo di una stringa.
 *
 * La funzione hash corrissonde alla sommatoria dei valori dei singoli caratteri che compongono la stringa
 * moltimplicati per la rispettiva potenza di 31 in base alla posizione del carattere.
 *
 * @param strToHash stringa da cui ricavare l'hash.
 */
int CMap_StringHash(char * strToHash){
	int len = strlen(strToHash);
	int hash = 0, i;
	for(i=0;i<len;i++){
		hash = hash*31 + strToHash[i];
	}
	return hash;
}
